import cv2 as cv
import numpy as np


def blur_demo(image):
    dst = cv.blur(image, (5, 5))  # 均值模糊，（1,10）是垂直方向模糊，（10,1）还水平方向模糊，适合随机噪声
    cv.imshow("blur_demo", dst)


def median_blur_demo(image):
    dst = cv.medianBlur(image, 5)  # 中值模糊，5*5的范围内，适合椒盐噪声
    cv.imshow("median_blur_demo", dst)
    return dst


def custom_demo(image):
    # kernel = np.ones([5, 5], np.uint8)/25  # 自定义卷积核，可实现均值滤波
    kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)  # 自定义卷积核，可实现锐化
    dst = cv.filter2D(image, -1, kernel=kernel)  # 第三个参数是有默认值的，如果不这样写，就还是默认值
    # 第一个参数表示待处理的输入图像。
    # 第二个参数表示目标图像深度，输入值为-1时，目标图像和原图像深度保持一致（默认值为-1）
    # 第三个参数表示单通道浮点矩阵。 如果想要应用不同的核于不同的通道，先用 cvSplit 函数分解图像到单个色彩通道上，然后单独处理。
    cv.imshow("custom_demo", dst)


def clamp(pv):
    if pv > 255:
        return 255
    if pv < 0:
        return 0
    else:
        return  pv


def gaussian_noise(image):
    h, w, c = image.shape
    for row in range(h):  # 完整的写法是range（h， 0， 1），即从0开始，每次增加1
        for col in range(w):
            for channel in range(c):
                s = np.random.normal(0, 20, 3)  # 随机数是从一个高斯函数中抽取的，0表示均值，20表示方差，3表示三个数
                b = image[row, col, 0]  # blur
                g = image[row, col, 1]  # green
                r = image[row, col, 2]  # red
                image[row, col, 0] = clamp(b + s[0])
                image[row, col, 1] = clamp(g + s[1])
                image[row, col, 2] = clamp(r + s[2])
    cv.imshow("gaussian_noise", image)


def bi_demo(image):   # 双边滤波
    dst = cv.bilateralFilter(image, 0, 80, 15)  # 双边滤波
    # 是结合图像的空间邻近度和像素值相似度的一种折中处理，同时考虑空域信息和灰度相似性，达到保边去噪的目的。但是由于保存了过多的高频信息，对于彩色图像里的高频噪声，双边滤波器不能够干净的滤掉，只能够对于低频信息进行较好的滤波
    # 第一个参数src表示待处理的输入图像。
    # 第二个参数d表示在过滤期间使用的每个像素邻域的直径。如果输入d非0，则sigmaSpace由d计算得出，如果sigmaColor没输入，则sigmaColor由sigmaSpace计算得出。
    # sigmaColor参数表示色彩空间的标准方差，一般尽可能大。较大的参数值意味着像素邻域内较远的颜色会混合在一起，从而产生更大面积的半相等颜色。
    # sigmaSpace参数表示坐标空间的标准方差(像素单位)，一般尽较小，越大意味着只要它们的颜色足够接近，远的像素都会相互影响。当d>0时，它指定邻域大小而不考虑sigmaSpace。否则，d与sigmaSpace成正比。
    cv.imshow("bi_demo", dst)
    return dst


def shift_demo(image):
    dst = cv.pyrMeanShiftFiltering(image, 10, 50)  # 均值漂移，sp和sr越大则图像越模糊
    # Mean Shift向量：偏移的均值向量，指向概率密度梯度的方向。这个算法是图像在色彩层面的平滑滤波，它可以中和色彩分布相近的颜色，平滑色彩细节，侵蚀掉面积较小的颜色区域
    # 第一个参数src，输入图像，8位，三通道的彩色图像，并不要求必须是RGB格式，HSV、YUV等Opencv中的彩色图像格式均可；
    # 第二个参数sp，定义的漂移物理空间半径大小；
    # 第三个参数sr，定义的漂移色彩空间半径大小；
    # 第四个参数dst，输出图像，跟输入src有同样的大小和数据格式；
    # 第五个参数maxLevel，定义金字塔的最大层数；
    # 第六个参数termcrit，定义的漂移迭代终止条件，可以设置为迭代次数满足终止，迭代目标与中心点偏差满足终止，或者两者的结合；
    cv.imshow("shift_demo", dst)


# src1 = cv.imread("imgs/test006.jpg")
# cv.imshow("lena1", src1)
# t1 = cv.getTickCount()
# gaussian_noise(src1)
# t2 = cv.getTickCount()
# time = (t2 - t1)/cv.getTickFrequency()
# print("time consume : %s" % (time * 1000))
# dst = cv.GaussianBlur(src1, (0, 0), 10)  # 高斯模糊
# 第一个参数src参数表示待处理的输入图像。
# 第二个参数ksize参数表示高斯滤波器模板大小。 ksize.width和ksize.height可以不同，但它们都必须是正数和奇数。或者，它们可以是零，即（0, 0），然后从σ计算出来。
# 第三个参数sigmaX参数表示 X方向上的高斯内核标准差。
# sigmaY参数表示 Y方向上的高斯内核标准差。 如果sigmaY为零，则设置为等于sigmaX，如果两个sigma均为零，则分别从ksize.width和ksize.height计算得到。
# 补：若ksize不为(0, 0)，则按照ksize计算，后面的sigmaX没有意义。若ksize为(0, 0)，则根据后面的sigmaX计算ksize
# cv.imshow("Gaussian_noise", dst)
# blur_demo(src1)
# src2 = cv.imread("imgs/test007.png")
# cv.imshow("lena2", src2)
# src3 = median_blur_demo(src2)
# custom_demo(src3)
src4 = cv.imread("imgs/test008.jpg")
cv.imshow("zll", src4)
shift_demo(src4)
# custom_demo(src4)
cv.waitKey(0)
cv.destroyAllWindows()
